﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BFlatCompile.Project
{
	internal static class JsonTokenizer
	{
		private static readonly HashSet<char> WHITESPACE = new HashSet<char>(new char[] { ' ', '\r', '\n', '\t' });

		public static string[] Tokenize(string text)
		{
			int index = 0;
			int length = text.Length;
			List<string> tokens = new List<string>();
			StringBuilder sb;
			char c;
			while (index < length)
			{
				c = text[index];
				if (WHITESPACE.Contains(c))
				{
					// do nothing
					++index;
				}
				else if (c == '"' || c == '\'')
				{
					sb = new StringBuilder();
					++index;
					sb.Append(c);
					while (index < length && text[index] != c)
					{
						sb.Append(text[index]);
						++index;

						if (text[index - 1] == '\\' && index < length)
						{
							sb.Append(text[index++]);
						}
					}
					sb.Append(c);
					index++;
					tokens.Add(sb.ToString());
				}
				else if (IsCharAlpha(c))
				{
					sb = new StringBuilder();
					++index;
					sb.Append(c);
					while (index < length && IsValidIdChar(text[index]))
					{
						sb.Append(text[index]);
						++index;
					}
					tokens.Add(sb.ToString());
				}
				else if (c == '{' || c == '}' || c == ':' || c == ',' || c == '[' || c == ']')
				{
					tokens.Add(c + "");
					++index;
				}
				else if (IsNumChar(c))
				{
					sb = new StringBuilder();
					++index;
					sb.Append(c);
					while (index < length && IsNumChar(text[index]))
					{
						sb.Append(text[index]);
						++index;
					}
					tokens.Add(sb.ToString());
				}
				else if (c == '/')
				{
					++index;
					if (index < length)
					{
						c = text[index];
						if (c == '*')
						{
							++index;
							while (index < length - 1 && text[index] != '*' && text[index + 1] != '/')
							{
								++index;
							}
							if (index == length)
							{
								throw new Exception("Unexpected EOF");
							}
						}
						else if (c == '/')
						{
							while (index < length && text[index] != '\n')
							{
								++index;
							}
						}
						else
						{
							throw new Exception("Unexpected char: " + c);
						}
					}
					else
					{
						throw new Exception("Unexpected EOF.");
					}
				}
				else
				{
					throw new Exception("Unexpected char: " + c);
				}
			}

			return tokens.ToArray();
		}

		private static bool IsNumChar(char c)
		{
			if (c >= '0' && c <= '9') return true;
			if (c == 'e' || c == '.' || c == '-') return true;
			return false;
		}

		private static bool IsCharAlpha(char c)
		{
			if (c >= 'a' && c <= 'z') return true;
			if (c >= 'A' && c <= 'Z') return true;
			if (c == '_') return true;
			return false;
		}

		private static bool IsValidIdChar(char c)
		{
			if (c >= '0' && c <= '9') return true;
			return IsCharAlpha(c);
		}
	}
}
